﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Model
{
    public class Book
    {
        private const string METADATA_FOLDER = "Data";

        private string title = "";
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        private List<Chapter> chapters = null;
        public List<Chapter> Chapters
        {
            get { return chapters; }
        }

        private List<Station> stations = null;
        public List<Station> Stations
        {
            get { return stations; }
        }

        private List<Part> parts = null;
        public List<Part> Parts
        {
            get { return parts; }
        }

        private List<Group> groups = null;
        public List<Group> Groups
        {
            get { return groups; }
        }

        private List<Quarter> quarters = null;
        public List<Quarter> Quarters
        {
            get { return quarters; }
        }

        private List<Bowing> bowings = null;
        public List<Bowing> Bowings
        {
            get { return bowings; }
        }

        private List<Page> pages = null;
        public List<Page> Pages
        {
            get { return pages; }
        }

        private List<Prostration> prostrations = null;
        public List<Prostration> Prostrations
        {
            get { return prostrations; }
        }

        private List<Verse> verses = null;
        public List<Verse> Verses
        {
            get { return verses; }
        }

        public int WordCount
        {
            get
            {
                int word_count = 0;
                foreach (Verse verse in this.verses)
                {
                    word_count += verse.Words.Count;
                }
                return word_count;
            }
        }

        private Dictionary<string, List<string>> unique_wordss = null;
        private List<string> UniqueWords
        {
            get
            {
                if (unique_wordss == null)
                {
                    unique_wordss = new Dictionary<string, List<string>>();
                }
                List<string> unique_words = new List<string>();

                foreach (Verse verse in verses)
                {
                    foreach (Word word in verse.Words)
                    {
                        if (!unique_words.Contains(word.Text))
                        {
                            unique_words.Add(word.Text);
                        }
                    }
                }
                return unique_words;
            }
        }

        //public int GetVerseNumber(int chapter_number, int verse_number_in_chapter)
        //{
        //    foreach (Chapter chapter in this.chapters)
        //    {
        //        if (chapter.Number == chapter_number)
        //        {
        //            foreach (Verse verse in chapter.Verses)
        //            {
        //                if (verse.NumberInChapter == verse_number_in_chapter)
        //                {
        //                    return verse.Number;
        //                }
        //            }
        //        }
        //    }
        //    return 0;
        //}

        public Book(string title, List<Verse> verses)
        {
            this.title = title;
            this.verses = verses;

            if (this.verses != null)
            {
                foreach (Verse verse in this.verses)
                {
                    verse.Book = this;
                }

                this.chapters = new List<Chapter>();
                this.stations = new List<Station>();
                this.parts = new List<Part>();
                this.groups = new List<Group>();
                this.quarters = new List<Quarter>();
                this.bowings = new List<Bowing>();
                this.pages = new List<Page>();
                this.prostrations = new List<Prostration>();

                this.min_words = 1;
                int word_count = 0;
                foreach (Verse verse in this.verses)
                {
                    word_count += verse.Words.Count;
                }
                this.max_words = word_count;

                this.min_letters = 1;
                this.max_letters = int.MaxValue; // verse.Letters is not populated yet

                SetupPartitions();
            }
        }
        private void SetupPartitions()
        {
            if (s_quran_metadata == null)
            {
                LoadQuranMetadata();
            }

            if (s_quran_metadata != null)
            {
                // setup Chapters
                for (int i = 0; i < s_quran_metadata.Chapters.Length; i++)
                {
                    int number = s_quran_metadata.Chapters[i].Number;
                    int verse_count = s_quran_metadata.Chapters[i].Verses;
                    int start_verse = s_quran_metadata.Chapters[i].StartVerse;
                    int end_verse = start_verse + verse_count;
                    string name = s_quran_metadata.Chapters[i].Name;
                    string transliterated_name = s_quran_metadata.Chapters[i].TransliteratedName;
                    string english_name = s_quran_metadata.Chapters[i].EnglishName;
                    int revelation_order = s_quran_metadata.Chapters[i].RevelationOrder;
                    RevelationPlace revelation_place = s_quran_metadata.Chapters[i].RevelationPlace;
                    int bowing_count = s_quran_metadata.Chapters[i].Bowings;

                    List<Verse> verses = new List<Verse>();
                    if (this.verses != null)
                    {
                        for (int j = start_verse; j < end_verse; j++)
                        {
                            int index = j - 1;
                            if ((index >= 0) && (index < this.verses.Count))
                            {
                                Verse verse = this.verses[index];
                                verses.Add(verse);
                            }
                            else
                            {
                                break;
                            }
                        }

                        Chapter chapter = new Chapter(number, name, transliterated_name, english_name, revelation_order, revelation_place, bowing_count, verses);
                        this.chapters.Add(chapter);
                    }
                }

                // setup Bowings
                for (int i = 0; i < s_quran_metadata.Bowings.Length; i++)
                {
                    int number = s_quran_metadata.Bowings[i].Number;
                    int start_chapter = s_quran_metadata.Bowings[i].StartChapter;
                    int start_chapter_verse = s_quran_metadata.Bowings[i].StartChapterVerse;

                    int start_verse = 0;
                    for (int j = 0; j < start_chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += start_chapter_verse;

                    int next_bowing_start_verse = 0;
                    if (i < s_quran_metadata.Bowings.Length - 1)
                    {
                        int next_bowing_start_chapter = s_quran_metadata.Bowings[i + 1].StartChapter;
                        int next_bowing_start_chapter_verse = s_quran_metadata.Bowings[i + 1].StartChapterVerse;
                        for (int j = 0; j < next_bowing_start_chapter - 1; j++)
                        {
                            next_bowing_start_verse += this.chapters[j].Verses.Count;
                        }
                        next_bowing_start_verse += next_bowing_start_chapter_verse;
                    }
                    else
                    {
                        next_bowing_start_verse = Verse.MAX_NUMBER + 1;
                    }

                    int end_verse = next_bowing_start_verse;

                    List<Verse> verses = new List<Verse>();
                    for (int j = start_verse; j < end_verse; j++)
                    {
                        int index = j - 1;
                        if ((index >= 0) && (index < this.verses.Count))
                        {
                            Verse verse = this.verses[index];
                            verses.Add(verse);
                        }
                        else
                        {
                            break;
                        }
                    }

                    Bowing bowing = new Bowing(number, verses);
                    this.bowings.Add(bowing);
                }

                // setup Group
                for (int i = 0; i < s_quran_metadata.Groups.Length; i++)
                {
                    int number = s_quran_metadata.Groups[i].Number;
                    int start_chapter = s_quran_metadata.Groups[i].StartChapter;
                    int start_chapter_verse = s_quran_metadata.Groups[i].StartChapterVerse;

                    int start_verse = 0;
                    for (int j = 0; j < start_chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += start_chapter_verse;

                    int next_group_start_verse = 0;
                    if (i < s_quran_metadata.Groups.Length - 1)
                    {
                        int next_group_start_chapter = s_quran_metadata.Groups[i + 1].StartChapter;
                        int next_group_start_chapter_verse = s_quran_metadata.Groups[i + 1].StartChapterVerse;
                        for (int j = 0; j < next_group_start_chapter - 1; j++)
                        {
                            next_group_start_verse += this.chapters[j].Verses.Count;
                        }
                        next_group_start_verse += next_group_start_chapter_verse;
                    }
                    else
                    {
                        next_group_start_verse = Verse.MAX_NUMBER + 1;
                    }

                    int end_verse = next_group_start_verse;

                    List<Verse> verses = new List<Verse>();
                    for (int j = start_verse; j < end_verse; j++)
                    {
                        int index = j - 1;
                        if ((index >= 0) && (index < this.verses.Count))
                        {
                            Verse verse = this.verses[index];
                            verses.Add(verse);
                        }
                        else
                        {
                            break;
                        }
                    }

                    Model.Group group = new Model.Group(number, verses);
                    this.groups.Add(group);
                }

                // setup Quarters
                for (int i = 0; i < s_quran_metadata.Quarters.Length; i++)
                {
                    int number = s_quran_metadata.Quarters[i].Number;
                    int start_chapter = s_quran_metadata.Quarters[i].StartChapter;
                    int start_chapter_verse = s_quran_metadata.Quarters[i].StartChapterVerse;

                    int start_verse = 0;
                    for (int j = 0; j < start_chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += start_chapter_verse;

                    int next_group_quarter_start_verse = 0;
                    if (i < s_quran_metadata.Quarters.Length - 1)
                    {
                        int next_group_quarter_start_chapter = s_quran_metadata.Quarters[i + 1].StartChapter;
                        int next_group_quarter_start_chapter_verse = s_quran_metadata.Quarters[i + 1].StartChapterVerse;
                        for (int j = 0; j < next_group_quarter_start_chapter - 1; j++)
                        {
                            next_group_quarter_start_verse += this.chapters[j].Verses.Count;
                        }
                        next_group_quarter_start_verse += next_group_quarter_start_chapter_verse;
                    }
                    else
                    {
                        next_group_quarter_start_verse = Verse.MAX_NUMBER + 1;
                    }

                    int end_verse = next_group_quarter_start_verse;

                    List<Verse> verses = new List<Verse>();
                    for (int j = start_verse; j < end_verse; j++)
                    {
                        int index = j - 1;
                        if ((index >= 0) && (index < this.verses.Count))
                        {
                            Verse verse = this.verses[index];
                            verses.Add(verse);
                        }
                        else
                        {
                            break;
                        }
                    }

                    Quarter group_quarter = new Quarter(number, verses);
                    this.quarters.Add(group_quarter);
                }

                // setup Pages
                for (int i = 0; i < s_quran_metadata.Pages.Length; i++)
                {
                    int number = s_quran_metadata.Pages[i].Number;
                    int start_chapter = s_quran_metadata.Pages[i].StartChapter;
                    int start_chapter_verse = s_quran_metadata.Pages[i].StartChapterVerse;

                    int start_verse = 0;
                    for (int j = 0; j < start_chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += start_chapter_verse;

                    int next_page_start_verse = 0;
                    if (i < s_quran_metadata.Pages.Length - 1)
                    {
                        int next_page_start_chapter = s_quran_metadata.Pages[i + 1].StartChapter;
                        int next_page_start_chapter_verse = s_quran_metadata.Pages[i + 1].StartChapterVerse;
                        for (int j = 0; j < next_page_start_chapter - 1; j++)
                        {
                            next_page_start_verse += this.chapters[j].Verses.Count;
                        }
                        next_page_start_verse += next_page_start_chapter_verse;
                    }
                    else
                    {
                        next_page_start_verse = Verse.MAX_NUMBER + 1;
                    }

                    int end_verse = next_page_start_verse;

                    List<Verse> verses = new List<Verse>();
                    for (int j = start_verse; j < end_verse; j++)
                    {
                        int index = j - 1;
                        if ((index >= 0) && (index < this.verses.Count))
                        {
                            Verse verse = this.verses[index];
                            verses.Add(verse);
                        }
                        else
                        {
                            break;
                        }
                    }

                    Page page = new Page(number, verses);
                    this.pages.Add(page);
                }

                // setup Parts
                for (int i = 0; i < s_quran_metadata.Parts.Length; i++)
                {
                    int number = s_quran_metadata.Parts[i].Number;
                    int start_chapter = s_quran_metadata.Parts[i].StartChapter;
                    int start_chapter_verse = s_quran_metadata.Parts[i].StartChapterVerse;

                    int start_verse = 0;
                    for (int j = 0; j < start_chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += start_chapter_verse;

                    int next_part_start_verse = 0;
                    if (i < s_quran_metadata.Parts.Length - 1)
                    {
                        int next_part_start_chapter = s_quran_metadata.Parts[i + 1].StartChapter;
                        int next_part_start_chapter_verse = s_quran_metadata.Parts[i + 1].StartChapterVerse;
                        for (int j = 0; j < next_part_start_chapter - 1; j++)
                        {
                            next_part_start_verse += this.chapters[j].Verses.Count;
                        }
                        next_part_start_verse += next_part_start_chapter_verse;
                    }
                    else
                    {
                        next_part_start_verse = Verse.MAX_NUMBER + 1;
                    }

                    int end_verse = next_part_start_verse;

                    List<Verse> verses = new List<Verse>();
                    for (int j = start_verse; j < end_verse; j++)
                    {
                        int index = j - 1;
                        if ((index >= 0) && (index < this.verses.Count))
                        {
                            Verse verse = this.verses[index];
                            verses.Add(verse);
                        }
                        else
                        {
                            break;
                        }
                    }

                    Part part = new Part(number, verses);
                    this.parts.Add(part);
                }

                // setup Stations
                for (int i = 0; i < s_quran_metadata.Stations.Length; i++)
                {
                    int number = s_quran_metadata.Stations[i].Number;
                    int start_chapter = s_quran_metadata.Stations[i].StartChapter;
                    int start_chapter_verse = s_quran_metadata.Stations[i].StartChapterVerse;

                    int start_verse = 0;
                    for (int j = 0; j < start_chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += start_chapter_verse;

                    int next_station_start_verse = 0;
                    if (i < s_quran_metadata.Stations.Length - 1)
                    {
                        int next_station_start_chapter = s_quran_metadata.Stations[i + 1].StartChapter;
                        int next_station_start_chapter_verse = s_quran_metadata.Stations[i + 1].StartChapterVerse;
                        for (int j = 0; j < next_station_start_chapter - 1; j++)
                        {
                            next_station_start_verse += this.chapters[j].Verses.Count;
                        }
                        next_station_start_verse += next_station_start_chapter_verse;
                    }
                    else
                    {
                        next_station_start_verse = Verse.MAX_NUMBER + 1;
                    }

                    int end_verse = next_station_start_verse;

                    List<Verse> verses = new List<Verse>();
                    for (int j = start_verse; j < end_verse; j++)
                    {
                        int index = j - 1;
                        if ((index >= 0) && (index < this.verses.Count))
                        {
                            Verse verse = this.verses[index];
                            verses.Add(verse);
                        }
                        else
                        {
                            break;
                        }
                    }

                    Station station = new Station(number, verses);
                    this.stations.Add(station);
                }

                // setup Prostration
                for (int i = 0; i < s_quran_metadata.Prostrations.Length; i++)
                {
                    int number = s_quran_metadata.Prostrations[i].Number;
                    int chapter = s_quran_metadata.Prostrations[i].Chapter;
                    int chapter_verse = s_quran_metadata.Prostrations[i].ChapterVerse;
                    ProstrationType type = s_quran_metadata.Prostrations[i].Type;

                    int start_verse = 0;
                    for (int j = 0; j < chapter - 1; j++)
                    {
                        start_verse += this.chapters[j].Verses.Count;
                    }
                    start_verse += chapter_verse;

                    if (this.verses.Count > start_verse - 1)
                    {
                        Verse verse = this.verses[start_verse - 1];
                        Prostration prostration = new Prostration(number, type, verse);
                        this.prostrations.Add(prostration);
                    }
                    else
                    {
                        break;
                    }
                }

                // update word/letter numbers and distances
                UpdatePartitions();
            }
        }
        private void UpdatePartitions()
        {
            // update verse/word/letter numbers
            UpdateNumbers();
        }
        private void UpdateNumbers()
        {
            int chapter_number = 1;
            int verse_number = 1;
            int word_number = 1;

            if (this.chapters != null)
            {
                // re-build book verses in case of chapter sorting
                this.verses.Clear();
                foreach (Chapter chapter in this.chapters)
                {
                    this.verses.AddRange(chapter.Verses);
                }

                // update verse/word/letter numbers
                foreach (Chapter chapter in this.chapters)
                {
                    chapter.Number = chapter_number++;

                    int verse_number_in_chapter = 1;
                    int word_number_in_chapter = 1;
                    foreach (Verse verse in chapter.Verses)
                    {
                        verse.Number = verse_number++;
                        verse.NumberInChapter = verse_number_in_chapter++;

                        int word_number_in_verse = 1;
                        foreach (Word word in verse.Words)
                        {
                            word.Number = word_number++;
                            word.NumberInChapter = word_number_in_chapter++;
                            word.NumberInVerse = word_number_in_verse++;
                        }
                    }
                }
            }
        }

        //chapter verses  start_verse name  transliterated_name english_name  revelation_place  revelation_order  bowings
        //part  start_chapter start_chapter_verse
        //group start_chapter start_chapter_verse
        //quarter start_chapter start_chapter_verse
        //station start_chapter start_chapter_verse
        //bowing start_chapter start_chapter_verse
        //page  start_chapter start_chapter_verse
        //prostration chapter chapter_verse type
        private class QuranMetadataChapter
        {
            public int Number;
            public int Verses;
            public int StartVerse;
            public string Name;
            public string TransliteratedName;
            public string EnglishName;
            public RevelationPlace RevelationPlace;
            public int RevelationOrder;
            public int Bowings;
        }
        private class QuranMetadataPart
        {
            public int Number;
            public int StartChapter;
            public int StartChapterVerse;
        }
        private class QuranMetadataGroup
        {
            public int Number;
            public int StartChapter;
            public int StartChapterVerse;
        }
        private class QuranMetadataQuarter
        {
            public int Number;
            public int StartChapter;
            public int StartChapterVerse;
        }
        private class QuranMetadataStation
        {
            public int Number;
            public int StartChapter;
            public int StartChapterVerse;
        }
        private class QuranMetadataBowing
        {
            public int Number;
            public int StartChapter;
            public int StartChapterVerse;
        }
        private class QuranMetadataPage
        {
            public int Number;
            public int StartChapter;
            public int StartChapterVerse;
        }
        private class QuranMetadataProstration
        {
            public int Number;
            public int Chapter;
            public int ChapterVerse;
            public ProstrationType Type;
        }
        private class QuranMetadata
        {
            public QuranMetadataChapter[] Chapters = new QuranMetadataChapter[Chapter.MAX_NUMBER];
            public QuranMetadataPart[] Parts = new QuranMetadataPart[Part.MAX_NUMBER];
            public QuranMetadataGroup[] Groups = new QuranMetadataGroup[Group.MAX_NUMBER];
            public QuranMetadataQuarter[] Quarters = new QuranMetadataQuarter[Quarter.MAX_NUMBER];
            public QuranMetadataStation[] Stations = new QuranMetadataStation[Station.MAX_NUMBER];
            public QuranMetadataBowing[] Bowings = new QuranMetadataBowing[Bowing.MAX_NUMBER];
            public QuranMetadataPage[] Pages = new QuranMetadataPage[Page.MAX_NUMBER];
            public QuranMetadataProstration[] Prostrations = new QuranMetadataProstration[Prostration.MAX_NUMBER];
        }
        private static QuranMetadata s_quran_metadata = null;
        private static void LoadQuranMetadata()
        {
            if (Directory.Exists(METADATA_FOLDER))
            {
                string filename = METADATA_FOLDER + "/" + "quran-metadata.txt";
                if (File.Exists(filename))
                {
                    s_quran_metadata = new QuranMetadata();
                    using (StreamReader reader = File.OpenText(filename))
                    {
                        try
                        {
                            //chapter verses  start_verse name  transliterated_name english_name  revelation_place  revelation_order  bowings
                            for (int i = 0; i < Chapter.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] parts = line.Split('\t');
                                QuranMetadataChapter info = new QuranMetadataChapter();
                                info.Number = int.Parse(parts[0]);
                                info.Verses = int.Parse(parts[1]);
                                info.StartVerse = int.Parse(parts[2]);
                                info.Name = parts[3];
                                info.TransliteratedName = parts[4];
                                info.EnglishName = parts[5];
                                info.RevelationPlace = (RevelationPlace)Enum.Parse(typeof(RevelationPlace), parts[6]);
                                info.RevelationOrder = int.Parse(parts[7]);
                                info.Bowings = int.Parse(parts[8]);
                                s_quran_metadata.Chapters[i] = info;
                            }

                            //part  start_chapter start_chapter_verse
                            for (int i = 0; i < Part.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] parts = line.Split('\t');
                                QuranMetadataPart info = new QuranMetadataPart();
                                info.Number = int.Parse(parts[0]);
                                info.StartChapter = int.Parse(parts[1]);
                                info.StartChapterVerse = int.Parse(parts[2]);
                                s_quran_metadata.Parts[i] = info;
                            }

                            //group start_chapter start_chapter_verse
                            for (int i = 0; i < Group.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] Groups = line.Split('\t');
                                QuranMetadataGroup info = new QuranMetadataGroup();
                                info.Number = int.Parse(Groups[0]);
                                info.StartChapter = int.Parse(Groups[1]);
                                info.StartChapterVerse = int.Parse(Groups[2]);
                                s_quran_metadata.Groups[i] = info;
                            }

                            //quarter start_chapter start_chapter_verse
                            for (int i = 0; i < Quarter.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] Quarters = line.Split('\t');
                                QuranMetadataQuarter info = new QuranMetadataQuarter();
                                info.Number = int.Parse(Quarters[0]);
                                info.StartChapter = int.Parse(Quarters[1]);
                                info.StartChapterVerse = int.Parse(Quarters[2]);
                                s_quran_metadata.Quarters[i] = info;
                            }

                            //station start_chapter start_chapter_verse
                            for (int i = 0; i < Station.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] Stations = line.Split('\t');
                                QuranMetadataStation info = new QuranMetadataStation();
                                info.Number = int.Parse(Stations[0]);
                                info.StartChapter = int.Parse(Stations[1]);
                                info.StartChapterVerse = int.Parse(Stations[2]);
                                s_quran_metadata.Stations[i] = info;
                            }

                            //bowing start_chapter start_chapter_verse
                            for (int i = 0; i < Bowing.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] Bowings = line.Split('\t');
                                QuranMetadataBowing info = new QuranMetadataBowing();
                                info.Number = int.Parse(Bowings[0]);
                                info.StartChapter = int.Parse(Bowings[1]);
                                info.StartChapterVerse = int.Parse(Bowings[2]);
                                s_quran_metadata.Bowings[i] = info;
                            }

                            //page  start_chapter start_chapter_verse
                            for (int i = 0; i < Page.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] Pages = line.Split('\t');
                                QuranMetadataPage info = new QuranMetadataPage();
                                info.Number = int.Parse(Pages[0]);
                                info.StartChapter = int.Parse(Pages[1]);
                                info.StartChapterVerse = int.Parse(Pages[2]);
                                s_quran_metadata.Pages[i] = info;
                            }

                            //prostration chapter chapter_verse type
                            for (int i = 0; i < Prostration.MAX_NUMBER; i++)
                            {
                                string line = reader.ReadLine();
                                if (line.Length == 0) { i--; continue; }
                                if (line.StartsWith("//")) { i--; continue; }
                                string[] parts = line.Split('\t');
                                QuranMetadataProstration info = new QuranMetadataProstration();
                                info.Number = int.Parse(parts[0]);
                                info.Chapter = int.Parse(parts[1]);
                                info.ChapterVerse = int.Parse(parts[2]);
                                info.Type = (ProstrationType)Enum.Parse(typeof(ProstrationType), parts[3]);
                                s_quran_metadata.Prostrations[i] = info;
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("LoadQuranMetadata: " + ex.Message);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Get chapters with ALL their verses used inside the parameter "verses".
        /// Chapters with some verses used in the parameter "verses" are not returned.  
        /// </summary>
        /// <param name="verses"></param>
        /// <returns></returns>
        public List<Chapter> GetChapters(List<Verse> verses)
        {
            List<Chapter> result = new List<Chapter>();
            Chapter chapter = null;
            foreach (Verse verse in verses)
            {
                if (chapter != verse.Chapter)
                {
                    chapter = verse.Chapter;
                    if (!result.Contains(chapter))
                    {
                        bool include_chapter = true;
                        foreach (Verse chapter_verse in chapter.Verses)
                        {
                            if (!verses.Contains(chapter_verse))
                            {
                                include_chapter = false;
                                break;
                            }
                        }

                        if (include_chapter)
                        {
                            result.Add(chapter);
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Get verses with ALL their words used inside the parameter "words".
        /// Verses with some words used in the parameter "words" are not returned.  
        /// </summary>
        /// <param name="words"></param>
        /// <returns></returns>
        public List<Verse> GetVerses(List<Word> words)
        {
            List<Verse> result = new List<Verse>();
            Verse verse = null;
            foreach (Word word in words)
            {
                if (verse != word.Verse)
                {
                    verse = word.Verse;
                    if (!result.Contains(verse))
                    {
                        bool include_verse = true;
                        foreach (Word verse_word in verse.Words)
                        {
                            if (!words.Contains(verse_word))
                            {
                                include_verse = false;
                                break;
                            }
                        }

                        if (include_verse)
                        {
                            result.Add(verse);
                        }
                    }
                }
            }
            return result;
        }

        private string text;
        public string Text
        {
            get
            {
                if (String.IsNullOrEmpty(text))
                {
                    StringBuilder str = new StringBuilder();
                    if (this.verses != null)
                    {
                        foreach (Verse verse in this.verses)
                        {
                            str.AppendLine(verse.Text);
                        }
                        str.Remove(str.Length - 2, 2);
                    }
                    this.text = str.ToString();
                }
                return this.text;
            }
        }

        private int min_words;
        public int MinWords
        {
            get { return min_words; }
        }
        private int max_words;
        public int MaxWords
        {
            get { return max_words; }
        }
        private int min_letters;
        public int MinLetters
        {
            get { return min_letters; }
        }
        private int max_letters;
        public int MaxLetters
        {
            get { return max_letters; }
        }

        // root words
        private Dictionary<string, List<Word>> root_words;
        public Dictionary<string, List<Word>> RootWords
        {
            get { return root_words; }
            set { root_words = value; }
        }
        // translation infos
        private Dictionary<string, TranslationInfo> translation_infos;
        public Dictionary<string, TranslationInfo> TranslationInfos
        {
            get { return translation_infos; }
            set { translation_infos = value; }
        }
        // recitation infos
        private Dictionary<string, RecitationInfo> recitation_infos;
        public Dictionary<string, RecitationInfo> RecitationInfos
        {
            get { return recitation_infos; }
            set { recitation_infos = value; }
        }

        // get verse range
        public List<Verse> GetVerses(int start, int end)
        {
            List<Verse> result = new List<Verse>();
            if (
                (start >= end)
                &&
                (start >= Verse.MIN_NUMBER && start <= Verse.MAX_NUMBER)
                &&
                (end >= Verse.MIN_NUMBER && end <= Verse.MAX_NUMBER)
                )
            {
                foreach (Verse verse in this.Verses)
                {
                    if ((verse.Number >= start) && (verse.Number <= end))
                    {
                        result.Add(verse);
                    }
                }
            }
            return result;
        }
        // get words
        public Dictionary<string, int> GetWordsWith(List<Verse> verses, string text, bool anywhere_in_word)
        {
            Dictionary<string, int> result = new Dictionary<string, int>();
            if (verses != null)
            {
                if (!String.IsNullOrEmpty(text))
                {
                    text = text.Trim();
                    if (!text.Contains(" "))
                    {
                        foreach (Verse verse in verses)
                        {
                            string[] word_texts = verse.Text.Split(' ');
                            foreach (string word_text in word_texts)
                            {
                                if (anywhere_in_word)
                                {
                                    if (word_text.Contains(text))
                                    {
                                        if (!result.ContainsKey(word_text))
                                        {
                                            result.Add(word_text, 1);
                                        }
                                        else
                                        {
                                            result[word_text]++;
                                        }
                                    }
                                }
                                else
                                {
                                    if (word_text.StartsWith(text))
                                    {
                                        if (!result.ContainsKey(word_text))
                                        {
                                            result.Add(word_text, 1);
                                        }
                                        else
                                        {
                                            result[word_text]++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
        public Dictionary<string, int> GetCurrentWords(List<Verse> verses, string text, bool anywhere_in_word)
        {
            Dictionary<string, int> result = new Dictionary<string, int>();
            if (!String.IsNullOrEmpty(text))
            {
                text = text.Trim();
                while (text.Contains("  "))
                {
                    text = text.Replace("  ", " ");
                }

                string[] text_words = text.Split(' ');
                foreach (Verse verse in verses)
                {
                    string[] words = verse.Text.Split(' ');
                    for (int i = 0; i < words.Length; i++)
                    {
                        bool is_text_matched = false;
                        if (text_words.Length == 1) // 1 text_word
                        {
                            if (anywhere_in_word)
                            {
                                if (words[i].Contains(text_words[0])) // start found
                                {
                                    is_text_matched = true;
                                }
                            }
                            else
                            {
                                if (words[i].StartsWith(text_words[0])) // start found
                                {
                                    is_text_matched = true;
                                }
                            }
                        }
                        else if (text_words.Length > 1)// more than 1 text_word
                        {
                            if (words[i].EndsWith(text_words[0])) // start found
                            {
                                if (words.Length >= (i + text_words.Length))
                                {
                                    // match text minus last word
                                    bool is_text_matched_minus_last_word = true;
                                    for (int j = 1; j < text_words.Length - 1; j++)
                                    {
                                        if (words[j + i] != text_words[j])
                                        {
                                            is_text_matched_minus_last_word = false;
                                            break;
                                        }
                                    }

                                    // is still true, check the last word
                                    if (is_text_matched_minus_last_word)
                                    {
                                        int last_j = text_words.Length - 1;
                                        if (words[last_j + i].StartsWith(text_words[last_j])) // last text_word
                                        {
                                            is_text_matched = true;
                                        }
                                    }
                                }
                            }
                        }

                        if (is_text_matched)
                        {
                            // skip all text but not found good_word in case it followed by good_word too
                            i += text_words.Length - 1;

                            // get last word variation
                            if (i < words.Length)
                            {
                                string good_word = words[i];
                                if (!result.ContainsKey(good_word))
                                {
                                    result.Add(good_word, 1);
                                }
                                else
                                {
                                    result[good_word]++;
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
        public Dictionary<string, int> GetNextWords(List<Verse> verses, string text, bool anywhere_in_word)
        {
            Dictionary<string, int> result = new Dictionary<string, int>();
            if (verses != null)
            {
                if (!String.IsNullOrEmpty(text))
                {
                    text = text.Trim();
                    while (text.Contains("  "))
                    {
                        text = text.Replace("  ", " ");
                    }

                    string[] text_words = text.Split(' ');
                    foreach (Verse verse in verses)
                    {
                        string[] words = verse.Text.Split(' ');
                        for (int i = 0; i < words.Length; i++)
                        {
                            bool start_of_text_words_found = false;
                            if (anywhere_in_word)
                            {
                                start_of_text_words_found = words[i].EndsWith(text_words[0]);
                            }
                            else
                            {
                                start_of_text_words_found = words[i].Equals(text_words[0]);
                            }

                            if (start_of_text_words_found)
                            {
                                if (words.Length >= (i + text_words.Length))
                                {
                                    // check rest of text_words if matching
                                    bool is_text_matched = true;
                                    for (int j = 1; j < text_words.Length; j++)
                                    {
                                        if (words[j + i] != text_words[j])
                                        {
                                            is_text_matched = false;
                                            break;
                                        }
                                    }

                                    if (is_text_matched)
                                    {
                                        // skip text_words
                                        i += text_words.Length;

                                        // add next word to result (if not added already)
                                        if (i < words.Length)
                                        {
                                            string good_word = words[i];
                                            if (!result.ContainsKey(good_word))
                                            {
                                                result.Add(good_word, 1);
                                            }
                                            else
                                            {
                                                result[good_word]++;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
        // get roots and related-words
        public List<string> GetRoots()
        {
            List<string> result = new List<string>();
            foreach (string key in this.RootWords.Keys)
            {
                result.Add(key);
            }
            return result;
        }
        public List<string> GetRoots(string word_text)
        {
            List<string> result = new List<string>();
            if (word_text.Length > 0)
            {
                Dictionary<string, List<Word>> root_words_dictionary = this.RootWords;
                if (root_words_dictionary != null)
                {
                    foreach (string key in root_words_dictionary.Keys)
                    {
                        List<Word> root_words = root_words_dictionary[key];
                        foreach (Word root_word in root_words)
                        {
                            Verse verse = this.Verses[root_word.Verse.Number - 1];
                            if (verse.Words.Count > root_word.NumberInVerse - 1)
                            {
                                Word verse_word = verse.Words[root_word.NumberInVerse - 1];
                                if (verse_word.Text == word_text)
                                {
                                    result.Add(key);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
        public List<string> GetRootsStartingWith(string text)
        {
            List<string> result = new List<string>();
            Dictionary<string, List<Word>> root_words_dictionary = this.RootWords;
            if (root_words_dictionary != null)
            {
                foreach (string key in root_words_dictionary.Keys)
                {
                    if (key.StartsWith(text))
                    {
                        result.Add(key);
                    }
                }
            }
            return result;
        }
        public List<string> GetRootsContaining(string text)
        {
            List<string> result = new List<string>();
            Dictionary<string, List<Word>> root_words_dictionary = this.RootWords;
            if (root_words_dictionary != null)
            {
                foreach (string key in root_words_dictionary.Keys)
                {
                    if (key.Contains(text))
                    {
                        result.Add(key);
                    }
                }
            }
            return result;
        }
        public string GetRoot(string word_text)
        {
            string result = "";
            if (word_text.Length > 0)
            {
                Dictionary<string, List<Word>> root_words_dictionary = this.RootWords;
                if (root_words_dictionary != null)
                {
                    string new_root = "";
                    bool found = false;
                    foreach (string key in root_words_dictionary.Keys)
                    {
                        List<Word> root_words = root_words_dictionary[key];
                        foreach (Word root_word in root_words)
                        {
                            Verse verse = this.Verses[root_word.Verse.Number - 1];

                            if (verse.Words.Count > root_word.NumberInVerse - 1)
                            {
                                Word verse_word = verse.Words[root_word.NumberInVerse - 1];
                                if (verse_word.Text == word_text)
                                {
                                    new_root = key;

                                    // only stop on proper root (3 or 4-letter long) or similar length to word_root, 
                                    found = (
                                                (key.Length >= 3)
                                                ||
                                                (key.Length == word_text.Length - 1)
                                                ||
                                                (key.Length == word_text.Length)
                                                ||
                                                (key.Length == word_text.Length + 1)
                                            );

                                    break;
                                }
                            }
                        }
                        if (found)
                        {
                            break;
                        }
                    }

                    // if nothing found
                    // find the first word containing word_text and get its root
                    if (!found)
                    {
                        foreach (string key in root_words_dictionary.Keys)
                        {
                            List<Word> root_words = root_words_dictionary[key];
                            foreach (Word root_word in root_words)
                            {
                                Verse verse = this.Verses[root_word.Verse.Number - 1];

                                if (verse.Words.Count > root_word.NumberInVerse - 1)
                                {
                                    Word verse_word = verse.Words[root_word.NumberInVerse - 1];
                                    if (verse_word.Text.Contains(word_text))
                                    {
                                        new_root = key;

                                        // only stop on proper root (3 or 4-letter long) or similar length to word_root, 
                                        found = (
                                                    (key.Length >= 3)
                                                    ||
                                                    (key.Length == word_text.Length - 1)
                                                    ||
                                                    (key.Length == word_text.Length)
                                                    ||
                                                    (key.Length == word_text.Length + 1)
                                                );

                                        break;
                                    }
                                }
                            }
                            if (found)
                            {
                                break;
                            }
                        }
                    }

                    // if nothing found
                    // find the first 80% similar word to word_text and get its root
                    if (!found)
                    {
                        foreach (string key in root_words_dictionary.Keys)
                        {
                            List<Word> root_words = root_words_dictionary[key];
                            foreach (Word root_word in root_words)
                            {
                                Verse verse = this.Verses[root_word.Verse.Number - 1];

                                if (verse.Words.Count > root_word.NumberInVerse - 1)
                                {
                                    Word verse_word = verse.Words[root_word.NumberInVerse - 1];
                                    if (verse_word.Text.IsSimilarTo(word_text, 0.80D))
                                    {
                                        new_root = key;

                                        // only stop on proper root (3 or 4-letter long) or similar length to word_root, 
                                        found = (
                                                    (key.Length >= 3)
                                                    ||
                                                    (key.Length == word_text.Length - 1)
                                                    ||
                                                    (key.Length == word_text.Length)
                                                    ||
                                                    (key.Length == word_text.Length + 1)
                                                );

                                        break;
                                    }
                                }
                            }
                            if (found)
                            {
                                break;
                            }
                        }
                    }

                    result = new_root;
                }
            }
            return result;
        }
        public List<string> GetRelatedWords(string word_text)
        {
            List<string> result = new List<string>();
            if (word_text.Length > 0)
            {
                Dictionary<string, List<Word>> root_words_dictionary = this.RootWords;
                if (root_words_dictionary != null)
                {
                    // try all roots in case word_text is a root
                    if (root_words_dictionary.ContainsKey(word_text))
                    {
                        List<Word> words = root_words_dictionary[word_text];
                        foreach (Word word in words)
                        {
                            Verse verse = this.Verses[word.Verse.Number - 1];
                            Word verse_word = verse.Words[word.NumberInVerse - 1];
                            if (!result.Contains(verse_word.Text))
                            {
                                result.Add(verse_word.Text);
                            }
                        }
                    }
                    else // if no such root, search for the matching root_word by its verse position and get its root and then get all root_words
                    {
                        string root = GetRoot(word_text);
                        if (!String.IsNullOrEmpty(root))
                        {
                            List<Word> root_words = root_words_dictionary[root];
                            foreach (Word root_word in root_words)
                            {
                                Verse verse = this.Verses[root_word.Verse.Number - 1];
                                Word verse_word = verse.Words[root_word.NumberInVerse - 1];
                                if (!result.Contains(verse_word.Text))
                                {
                                    result.Add(verse_word.Text);
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
        public List<Verse> GetRelatedVerses(string word_text)
        {
            List<Verse> result = new List<Verse>();
            if (word_text.Length > 0)
            {
                Dictionary<string, List<Word>> root_words_dictionary = this.RootWords;
                if (root_words_dictionary != null)
                {
                    // try all roots in case word_text is a root
                    if (root_words_dictionary.ContainsKey(word_text))
                    {
                        List<Word> words = root_words_dictionary[word_text];
                        foreach (Word word in words)
                        {
                            Verse verse = this.Verses[word.Verse.Number - 1];
                            if (!result.Contains(verse))
                            {
                                result.Add(verse);
                            }
                        }
                    }
                    else // if no such root, search for the matching root_word by its verse position and get its root and then get all root_words
                    {
                        string root = GetRoot(word_text);
                        if (!String.IsNullOrEmpty(root))
                        {
                            List<Word> root_words = root_words_dictionary[root];
                            foreach (Word root_word in root_words)
                            {
                                Verse verse = this.Verses[root_word.Verse.Number - 1];
                                if (!result.Contains(verse))
                                {
                                    result.Add(verse);
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
    }
}
